﻿// Copyright (c) 2008, Colin Burn
// All rights reserved.
// 
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// 
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// 
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// 
// * Neither the name of WTCL nor the names of its contributors may be
// used to endorse or promote products derived from this software
// without specific prior written permission.
// 
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
// OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
// DAMAGE.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;

using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

using WTCL.Charts;
using WTCL.Data.Series.Group;
using WTCL.Data.Series.ValueVsValue;
using WTCL.Data.Series.ValueVsValueWithRange;
using WTCL.Data.Type;

namespace ChartTest
{
    public partial class Window1 : Window
    {
        private ValueSeries<double, double, double, double> series1 = new ValueSeries<double, double, double, double>();
        private ValueSeries<double, double, double, double> series2 = new ValueSeries<double, double, double, double>();

        private MonotonicXValueSeries<double, double, double, double> monotonicXSeries1 = new MonotonicXValueSeries<double, double, double, double>();
        private MonotonicXValueSeries<double, double, double, double> monotonicXSeries2 = new MonotonicXValueSeries<double, double, double, double>();

        private MonotonicXValueSeries<DateTime, TimeSpan, double, double> seriesX = new MonotonicXValueSeries<DateTime, TimeSpan, double, double>();
        private ValueWithRangeSeries<DateTime, TimeSpan, double, double> seriesTimeValueWithRange = new ValueWithRangeSeries<DateTime, TimeSpan, double, double>();

        private GroupSeries<double, double> series1b = new GroupSeries<double, double>();
        private GroupSeries<double, double> series2b = new GroupSeries<double, double>();

        private Queue<DateTime> updates = new Queue<DateTime>();

        public Window1()
        {
            InitializeComponent();

            Color color1 = Color.FromArgb(0x80, 0xFF, 0x00, 0x00);
            Color color2 = Color.FromArgb(0x80, 0x00, 0x00, 0xFF);
            Color color3 = Color.FromArgb(0x10, 0x20, 0x20, 0x20);

            for (int i = 0; i < 25; ++i)
            {
                series1b.Samples.Add(new GroupData<double, double>(i.ToString(), new DoubleDataType(1)));
                series2b.Samples.Add(new GroupData<double, double>(i.ToString(), new DoubleDataType(1)));
            }

            series1.Brush = new SolidColorBrush(color1);
            series2.Brush = new SolidColorBrush(color2);
            monotonicXSeries1.Brush = new SolidColorBrush(color1);
            monotonicXSeries2.Brush = new SolidColorBrush(color2);
            seriesX.Brush = new SolidColorBrush(color1);
            seriesTimeValueWithRange.Brush = new SolidColorBrush(color1);
            seriesTimeValueWithRange.RangeBrush = new SolidColorBrush(color3);

            series1.Pen = new Pen(new SolidColorBrush(color1), 1);
            series2.Pen = new Pen(new SolidColorBrush(color2), 1);
            monotonicXSeries1.Pen = new Pen(new SolidColorBrush(color1), 1);
            monotonicXSeries2.Pen = new Pen(new SolidColorBrush(color2), 1);
            seriesX.Pen = new Pen(new SolidColorBrush(color1), 1);
            seriesTimeValueWithRange.Pen = new Pen(new SolidColorBrush(color1), 2);
            seriesTimeValueWithRange.RangePen = new Pen(new SolidColorBrush(color3), 1);

            series1.YMinimum = new DoubleDataType(-1.5);
            series1.YMaximum = new DoubleDataType(1.5);

            monotonicXSeries1.YMinimum = new DoubleDataType(-1.5);
            monotonicXSeries1.YMaximum = new DoubleDataType(1.5);
            
            series2.YMinimum = new DoubleDataType(-1.5);
            series2.YMaximum = new DoubleDataType(1.5);

            monotonicXSeries2.YMinimum = new DoubleDataType(-1.5);
            monotonicXSeries2.YMaximum = new DoubleDataType(1.5);

            series1b.Brush = new SolidColorBrush(color1);
            series2b.Brush = new SolidColorBrush(color2);
            series1b.Pen = new Pen(new SolidColorBrush(color1), 1);
            series2b.Pen = new Pen(new SolidColorBrush(color2), 1);

            series1b.YMinimum = new DoubleDataType(-1.5);
            series1b.YMaximum = new DoubleDataType(1.5);

            series2b.YMinimum = new DoubleDataType(-1.5);
            series2b.YMaximum = new DoubleDataType(1.5);

            this.lineChart.Series.Add(monotonicXSeries1);
            this.lineChart.Series.Add(monotonicXSeries2);

            this.scatterPlotChart.Series.Add(series1);
            this.scatterPlotChart.Series.Add(series2);

            this.barChart.Series.Add(series1b);
            this.barChart.Series.Add(series2b);

            this.stackedBarChart.Series.Add(series1b);
            this.stackedBarChart.Series.Add(series2b);

            this.areaChart.Series.Add(monotonicXSeries1);
            this.areaChart.Series.Add(monotonicXSeries2);

            this.timeValueWithRangeChart.Series.Add(seriesTimeValueWithRange);
            this.timeValueWithRangeChart.Series.Add(seriesTimeValueWithRange);

            this.xChart.Series.Add(this.seriesX);

            Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new VoidDelegate(UpdateSeries));
        }

        private delegate void VoidDelegate();

        private DateTime lastUpdate = DateTime.Now;
        private Random random = new Random();
        private Queue<double> oldValues = new Queue<double>();

        private void UpdateSeries()
        {
            DateTime now = DateTime.Now;

            int i = this.series1.Samples.Count;

            series1.Samples.Add(new ValueData<double, double, double, double>(new DoubleDataType(i), new DoubleDataType(Math.Sin(i / (Math.PI)))));
            series2.Samples.Add(new ValueData<double, double, double, double>(new DoubleDataType(i), new DoubleDataType(Math.Sin(i / (3 * Math.PI)))));

            this.monotonicXSeries1.XMinimum = new DoubleDataType(i - 100);
            this.monotonicXSeries1.XMaximum = new DoubleDataType(i);

            this.monotonicXSeries2.XMinimum = new DoubleDataType(i - 100);
            this.monotonicXSeries2.XMaximum = new DoubleDataType(i);

            monotonicXSeries1.Samples.Add(new ValueData<double, double, double, double>(new DoubleDataType(i), new DoubleDataType(Math.Sin(i / (Math.PI)))));
            monotonicXSeries2.Samples.Add(new ValueData<double, double, double, double>(new DoubleDataType(i), new DoubleDataType(Math.Sin(i / (3 * Math.PI)))));

            series1b.Samples[i % series1b.Samples.Count].Value = new DoubleDataType(Math.Sin(i / (Math.PI)));
            series2b.Samples[i % series2b.Samples.Count].Value = new DoubleDataType(Math.Sin(i / (3 * Math.PI)));

            updates.Enqueue(now);

            if (updates.Count > 100)
            {
                updates.Dequeue();
            }

            if (now != updates.Peek())
            {
                double fps = updates.Count / (now - updates.Peek()).TotalSeconds;

                this.message.Content = string.Format("Updates per second: {0:0.00} Count: {1}", fps, series1.Samples.Count);

                seriesX.Samples.Add(new ValueData<DateTime, TimeSpan, double, double>(new TimeDataType(now), new DoubleDataType(fps)));
            }

            for (DateTime current = this.lastUpdate; current < now; current += new TimeSpan(0, 0, 0, 0, 100))
            {
                double d = random.NextDouble();

                this.oldValues.Enqueue(d);
                if (this.oldValues.Count > 30)
                {
                    this.oldValues.Dequeue();
                }

                double sum = 0;
                foreach (double val in this.oldValues)
                {
                    sum += val;
                }

                double ave = sum / this.oldValues.Count;

                this.seriesTimeValueWithRange.XMinimum = new TimeDataType(now - new TimeSpan(0, 0, 3));
                this.seriesTimeValueWithRange.XMaximum = new TimeDataType(now);
                this.seriesTimeValueWithRange.Samples.Add(new ValueWithRangeData<DateTime, TimeSpan, double, double>(new TimeDataType(current), new DoubleDataType(d), new DoubleDataType(ave - 0.75), new DoubleDataType(ave + 0.75)));
            }

            this.lastUpdate = now;

            Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new VoidDelegate(UpdateSeries));
        }
    }
}